﻿@*******************************************************************************************************
    //  GraphMeasurements.cshtml - Gbtc
    //
    //  Copyright © 2016, Grid Protection Alliance.  All Rights Reserved.
    //
    //  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
    //  the NOTICE file distributed with this work for additional information regarding copyright ownership.
    //  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
    //  file except in compliance with the License. You may obtain a copy of the License at:
    //
    //      http://opensource.org/licenses/MIT
    //
    //  Unless agreed to in writing, the subject software distributed under the License is distributed on an
    //  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
    //  License for the specific language governing permissions and limitations.
    //
    //  Code Modification History:
    //  ----------------------------------------------------------------------------------------------------
    //  01/15/2016 - J. Ritchie Carroll
    //       Generated original version of source code.
    //
    //*****************************************************************************************************@
@using PQDashboard.Model
@using GSF.Web.Model
@using RazorEngine.Templating
@model AppModel
@{
    Layout = "";
    ViewBag.Title = "Graph Measurements";
    ViewBag.SetFullWidth = true;
}

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta charset="utf-8" />
    <meta http-equiv="cache-control" content="max-age=0" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
    <meta http-equiv="pragma" content="no-cache" />

    <style>
        html {
            overflow-x: hidden;
            height: 100%;
        }

        body {
            overflow-x: hidden;
            overflow-y: hidden;
            height: 100%;
        }

        .col-md-3 {
            padding-left: 0;
            padding-right: 0;
        }

        .list-group {
            padding-left: 0px;
            margin-left: 0px;
        }

        .list-group-item {
            padding-left: 0px;
            margin-left: 0px;
        }

        .panel-group {
            width: 95%;
        }

        .panel-body {
            padding: 0;
        }

        .panel-heading {
            padding: 0;
        }

        ul.nowrap {
            overflow: auto;
        }

        li.nowrap {
            white-space: nowrap;
        }

        .content {
            padding-left: 10px;
        }

        #toggleCanvasButton {
            color: rgb(255, 255, 255);
            padding-bottom: 0px;
            padding-left: 3px;
            font-size: small;
            margin-left: -10px;
        }

        #toggleCanvasIcon {
            -webkit-transition: all 1.0s ease;
            -moz-transition: all 1.0s ease;
            -o-transition: all 1.0s ease;
            transition: all 1.0s ease;
        }

        .row-offcanvas.active #toggleCanvasIcon {
            filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
            -webkit-transform: scale(1, -1);
            -moz-transform: scale(1, -1);
            -ms-transform: scale(1, -1);
            -o-transform: scale(1, -1);
            transform: scale(1, -1);
            padding-bottom: 1px;
            padding-left: 1px;
        }

        @@media screen and (min-width: 1250px) {
            #toggleCanvasIcon {
                filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
                -webkit-transform: scale(-1, 1);
                -moz-transform: scale(-1, 1);
                -ms-transform: scale(-1, 1);
                -o-transform: scale(-1, 1);
                transform: scale(-1, 1);
            }

            .row-offcanvas {
                position: relative;
                -webkit-transition: all .25s ease-out;
                -moz-transition: all .25s ease-out;
                transition: all .25s ease-out;
            }

            .row-offcanvas-right {
                right: 20%;
            }

            .row-offcanvas-left {
                left: 20%;
            }

            .row-offcanvas-right .sidebar-offcanvas {
                right: -20%; /* 3 columns */
                background-color: rgb(255, 255, 255);
            }

            .row-offcanvas-left .sidebar-offcanvas {
                left: -20%; /* 3 columns */
                background-color: rgb(255, 255, 255);
            }

            .row-offcanvas-right.active {
                right: 0; /* 3 columns */
            }

            .row-offcanvas-left.active {
                left: 0; /* 3 columns */
            }

            .row-offcanvas-right.active .sidebar-offcanvas {
                background-color: rgb(254, 254, 254);
            }

            .row-offcanvas-left.active .sidebar-offcanvas {
                background-color: rgb(254, 254, 254);
            }

            .row-offcanvas .content {
                width: 80%; /* 9 columns */
                -webkit-transition: all .25s ease-out;
                -moz-transition: all .25s ease-out;
                transition: all .25s ease-out;
            }

            .row-offcanvas.active .content {
                width: 100%; /* 12 columns */
            }

            .sidebar-offcanvas {
                position: absolute;
                top: 0;
                width: 20%; /* 3 columns */
            }
        }

        @@media screen and (max-width: 1249px) {
            #toggleCanvasIcon {
                filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
                -webkit-transform: scale(1, -1);
                -moz-transform: scale(1, -1);
                -ms-transform: scale(1, -1);
                -o-transform: scale(1, -1);
                transform: scale(1, -1);
                padding-bottom: 1px;
                padding-left: 1px;
            }

            .row-offcanvas {
                position: relative;
                -webkit-transition: all .25s ease-out;
                -moz-transition: all .25s ease-out;
                transition: all .25s ease-out;
            }

            .row-offcanvas-right {
                right: 0;
            }

            .row-offcanvas-left {
                left: 0;
            }

            .row-offcanvas-right .sidebar-offcanvas {
                right: -50%; /* 6 columns */
            }

            .row-offcanvas-left .sidebar-offcanvas {
                left: -50%; /* 6 columns */
            }

            .row-offcanvas-right.active {
                right: 50%; /* 6 columns */
            }

            .row-offcanvas-left.active {
                left: 50%; /* 6 columns */
            }

            .sidebar-offcanvas {
                position: absolute;
                top: 0;
                width: 50%; /* 6 columns */
            }
        }
    </style>

    <link rel="stylesheet" href="~/Content/bootstrap/theme.css" />
    <link rel="stylesheet" type="text/css" href="~/Content/font-awesome.css" />
    <link rel="stylesheet" href="~/Content/bootstrap-3.3.2.min.css" />
    <link rel="stylesheet" href="~/Content/themes/redmond/jquery-ui.css" />
    <link rel="stylesheet" href="~/Scripts/PrimeUI/primeui.min.css" />
    <link rel="stylesheet" href="~/Content/jquery.multiselect.css" />
    <link rel="stylesheet" href="~/Content/jquery.multiselect.filter.css" />
    <link rel="stylesheet" href="~/Content/Default.css" type="text/css" />
    <link rel="stylesheet" href="~/Content/bootstrap-multiselect.css" />
    <link rel="stylesheet" type="text/css" href="~/Scripts/Leaflet/leaflet1.0.css" />
    <script type="text/javascript" src="~/Scripts/jquery-2.1.1.js"></script>
    <script type="text/javascript" src="~/Scripts/jquery-ui.js"></script>
    <script src="~/Scripts/jquery.signalR-2.2.0.js"></script>

    <script type="text/javascript" src="~/Scripts/jquery.blockUI.js"></script>
    <script type="text/javascript" src="~/Scripts/jquery.sparkline.js"></script>
    <script type="text/javascript" src="~/Scripts/bootstrap-3.3.2.min.js"></script>
    <script type="text/javascript" src="~/Scripts/bootstrap-multiselect.js"></script>
    <script type="text/javascript" src="~/Scripts/PrimeUI/primeui.js"></script>
    <script type="text/javascript" src="~/Scripts/jquery.multiselect.js"></script>
    <script type="text/javascript" src="~/Scripts/jquery.multiselect.filter.js"></script>
    <script src="~/signalr/hubs"></script>
    <script type="text/javascript" src="~/Scripts/Site.js"></script>
    <script type="text/javascript" src="~/Scripts/jstorage.js"></script>
    <script type="text/javascript" src="~/Scripts/moment.js"></script>
    <script>
        const DateFormat = "@Model.Global.DateFormat";
        const TimeFormat = "@Model.Global.TimeFormat";
        const DateTimeFormat = "@Model.Global.DateTimeFormat";
    </script>
</head>


<body>

    <div class="container-fluid">
        <div class="row row-offcanvas row-offcanvas-left">
            <div class="sidebar-offcanvas toggled" style="overflow-y: auto" id="sidebar" role="navigation">
                <div class="panel-group">
                    <div class="panel panel-default">
                        <div class="panel-heading" style="padding: 5px">
                            <h4 class="panel-title">
                                <a data-toggle="collapse" href="#collapse1">Settings</a>
                            </h4>
                        </div>
                        <div id="collapse1" class="panel-collapse collapse" style="margin: 5px">
                            <label for="filterstring">Selected Measurements:</label>
                            <textarea id="filterstring" class="form-control" readonly></textarea>
                            <label for="datapoints">Number of Data Points to Plot:</label>
                            <input type="number" id="datapoints" class="form-control" />
                            <label for="refreshinterval">Data Refresh Interval(ms):</label>
                            <input type="number" id="refreshinterval" class="form-control" />
                            <label for="statrefreshinterval">Stat Refresh Interval(ms):</label>
                            <input type="number" id="statrefreshinterval" class="form-control" />
                            <hr class="half-break" />
                            <button type="button" class="btn btn-primary btn-xs" onclick="updateSettings()">Update Settings</button>
                        </div>
                    </div>
                </div>
                <div class="panel-group">
                    <div class="panel panel-default">
                        <div class="panel-heading" style="padding: 5px">
                            <h4 class="panel-title">
                                <a data-toggle="collapse" href="#collapse2">Legend</a>
                            </h4>
                        </div>
                        <div id="collapse2" class="panel-collapse collapse" style="margin: 5px">
                            <div id="legend" style="overflow-y: scroll"></div>
                        </div>
                    </div>
                </div>
                <div id="devicelist" class="panel-group">
                    <div id="devicelistinner" class="panel panel-default">

                    </div>
                </div>
            </div><!--/span-->
            <div class="content">
                <div class="pull-left">
                    <button type="button" class="btn btn-primary btn-xs" data-toggle="offcanvas" id="toggleCanvasButton" title="Toggle Menu"><span class="glyphicon glyphicon-expand" id="toggleCanvasIcon"></span></button>
                    <button type="button" class="btn btn-primary btn-xs" onclick="resetFilter()">Clear Signals</button>
                </div>
                <br />
                <br />
                <div class="text-center" id="graphwrapper">
                    <div id="placeholder" style="width: 100%;"></div>
                </div>
                <div id="modals">
                </div>
            </div><!--/span-->
        </div><!--/row-->
    </div><!-- /.container -->
</body>


<script src="~/Scripts/flot/jquery.flot.js"></script>
<script src="~/Scripts/flot/jquery.flot.crosshair.js"></script>
<script src="~/Scripts/flot/jquery.flot.navigate.js"></script>
<script src="~/Scripts/flot/jquery.flot.resize.js"></script>
<script src="~/Scripts/flot/jquery.flot.selection.js"></script>
<script src="~/Scripts/flot/jquery.flot.time.js"></script>
<script src="~/Scripts/flot/jquery.flot.axislabels.js"></script>
<script src="@Url.Content("@GSF/Web/Model/Scripts/gsf.web.client.js")"></script>

<script>
    var plot;
    var updateInterval = 250;
    var plotData = [];
    var plotDataTemplate = [];
    var deviceData = [];
    var measurementDetails = [];
    var statFilter = "";
    var stats = [];
    var lightFilter = "";
    var parentDevice = [];
    var statRefreshInterval;
    var dataRefreshInterval;
    var dataPointNumber;

    $(window).on("messageVisibiltyChanged", function (event) {
        setTimeout(function () {
            setHeights();
        }, 200);
    });

    $(function () {
        setHeights();

        $(window).resize(function () { setHeights() });

        $('[data-toggle=offcanvas]').click(function () {
            if ($('.sidebar-offcanvas').css('background-color') == 'rgb(255, 255, 255)') {
                $('.list-group-item').attr('tabindex', '-1');
            } else {
                $('.list-group-item').attr('tabindex', '');
            }
            $('.row-offcanvas').toggleClass('active');
        });

        $(window).on("hubConnected", function (event) {
            dataHub.initializeSubscriptions();
        });

        $(window).unload(function () {
            if (!hubIsConnected)
                return;

            dataHub.terminateSubscriptions();
        });

        // Client function called from the dataHub when meta data gets recieved
        dataHubClient.metaDataReceived = function () {
            getStats();
            buildModal("Subscription");
            buildDeviceList();
            buildPlotArray();
        }


    });

    // Helper functions

    // used to create array of objects that will be used to maintain plot data
    function buildPlotArray() {
        dataHub.getMeasurementDetails().done(function (data) {
            measurementDetails = data;
            //console.log(data)
            measurementDetails.sort(function (a, b) {
                if (a.ID > b.ID) return 1;
                if (a.ID < b.ID) return -1;

                return 0;
            });

            $.each(measurementDetails, function (i, md) {
                var acronym = null;
                if (md.DeviceAcronym != null)
                    acronym = md.DeviceAcronym.replace(/!/g, "-").replace(md.DeviceAcronym.split('!')[0] + '-', "");
                if (md.ID.indexOf("STAT") < 0 && md.SignalAcronym != "DIGI" && md.SignalAcronym != "FLAG" && md.SignalAcronym != "QUAL" && md.SignalAcronym != "STAT" && md.SignalAcronym != "ALOG") {
                    var sigref = md.SignalReference.replace(/!/g, "-");
                    sigref = sigref.replace(md.DeviceAcronym.replace(/!/g, "-") + "-", "");
                    var id = md.ID.split(":")[1];
                    lightFilter += md.ID + ';';
                    $('#tb' + acronym).append('<tr title="' + md.Description + '"><td nowrap><label class="checkbox-inline"><input type="checkbox" id="cb' + md.ID + '" value="' + md.ID + '#' + md.SignalID + '" hub-dependent>' + id + '</td><td>' + sigref + '</td><td>' + md.SignalAcronym + '</td></tr>');

                    //$('<label />', { 'for': 'cb' + md.ID, text: md.SignalReference +  ' - ' + md.SignalAcronym }).appendTo(checklist);
                    //$('<br/>').appendTo(checklist);
                    var yaxisNum = 1;
                    if (md.SignalAcronym == "VPHM") yaxisNum = 2;
                    else if (md.SignalAcronym == "IPHM") yaxisNum = 3;
                    else if (md.SignalAcronym.indexOf("A") >= 0) yaxisNum = 4;


                    plotDataTemplate.push({ label: md.SignalID, yaxis: yaxisNum, data: [] });
                }
                else if (md.SignalAcronym == "STAT") {
                    if (md.Enabled == true) {
                        statFilter += md.ID + ';';
                        stats.push({ deviceAcronym: md.DeviceAcronym, id: md.SignalID, value: null, timestamp: null, description: md.Description });
                        var index = stats.length - 1;
                        if (parentDevice.indexOf(md.DeviceAcronym) != -1)
                            $('#stat' + md.DeviceAcronym).append('<tr><td>' + md.ID.split(":")[1] + '</td><td id="des' + md.SignalID + '">' + md.Description + '</td><td><span id="val' + md.SignalID + '" >' + stats[index].value + '</span></td><td><span id="time' + md.SignalID + '" >' + stats[index].timestamp + '</span></td></tr>');
                        else if (md.DeviceAcronym != null) {
                            $('#stat' + acronym).append('<tr><td>' + md.ID.split(":")[1] + '</td><td id="des' + md.SignalID + '">' + md.Description + '</td><td><span id="val' + md.SignalID + '" >' + stats[index].value + '</span></td><td><span id="time' + md.SignalID + '" >' + stats[index].timestamp + '</span></td></tr>');
                        }
                    }
                }

            });
            $('input[type=checkbox]').each(function () {
                $(this).change(function () { updateFilter() });

            });
            checkCookie();

            //console.log(stats);
            dataHub.statSubscribe(statFilter);

            $('#devicelist').tooltip();

            dataHub.getMeasurements().done(function (data) {
                buildPlot(data);
                update();
            });
        }).fail(function (error) {
            showErrorMessage(error);
        });;

    }

    // used to build the device/stream list
    function buildDeviceList() {
        dataHub.getDeviceDetails().done(function (data) {
            //console.log(data);
            deviceData = data;
            deviceData.sort(function (a, b) {
                if (a.Acronym > b.Acronym) return 1;
                if (a.Acronym < b.Acronym) return -1;

                return 0;
            });
            $.each(deviceData, function (key, dd) {
                if (!dd.Enabled) return;
                if (dd.ParentAcronym === "") dd.ParentAcronym = "Others";
                if (parentDevice.indexOf(dd.ParentAcronym) == -1) {
                    parentDevice.push(dd.ParentAcronym);
                    $('#devicelistinner').append('<div class="panel-heading"><img id="img' + dd.ParentAcronym + '" src="@Url.Content("~/Images/StatusLights/Small/greylight.png")" style ="padding-left: 6px" /><button class="btn btn-link btn-sm" style="font-size: x-small;" data-toggle="collapse" data-parent="#devicelist" data-target="#dd' + dd.ParentAcronym + '">' + dd.ParentAcronym + '</button><button id="btn' + dd.ParentAcronym + '" class="btn btn-xs badge pull-right" style="font-size: xx-small; margin: 5px 15px 0 0">Stats</button><div id="dd' + dd.ParentAcronym + '" class="panel-collapse collapse in"><div class="panel-body"><ul class="list-group nowrap" id="parent' + dd.ParentAcronym + '"></ul></div></div></div>');
                    buildModal(dd.ParentAcronym);
                    $('#btn' + dd.ParentAcronym).on("click", function () {
                        $('#mod' + dd.ParentAcronym).modal('show');
                    });

                }

                var acronym = dd.Acronym.replace(/!/g, "-").replace(dd.Acronym.split('!')[0] + '-', "");
                $('#parent' + dd.ParentAcronym).append('<li class="list-group-item nowrap" ><img id="img' + acronym + '" src="@Url.Content("~/Images/StatusLights/Small/greylight.png")" style="padding-left: 5px" /><button class="btn btn-link btn-sm" style="font-size: x-small;" title="' + dd.Name + '" data-toggle="collapse" data-target="#dd' + acronym + '">' + acronym + '</button><button id="btn' + acronym + '" class="btn btn-xs badge" style="font-size: xx-small;">Stats</button><div id="dd' + acronym + '" class="collapse"><table class="table" style="width:15%; font-size: x-small;" id="tb' + acronym + '"  ><th>ID</th><th>Stream</th><th>Signal</th></table></div></li>');
                buildModal(acronym);
                $('#btn' + acronym).on("click", function () {
                    $('#mod' + acronym).modal('show');
                });
                $('#showSubscriptionStats').on("click", function () {
                    $('#modSubscription').modal('show');
                });

            });

        }).fail(function (error) {
            showErrorMessage(error);
        });

    }

    // this will get the first set of data and build the plot
    function buildPlot(data) {
        $.each(data, function (i, d) {
            for (var j in plotData) {
                if (plotData[j].label == d.ID) {
                    plotData[j].data.push([d.Timestamp, d.Value]);
                }
            }
        });


        plot = $.plot("#placeholder", plotData, {
            series: {
                shadowSize: 0
            },
            yaxes: [{
                show: true,
                position: "left",
                axisLabel: "Frequency"
            }, {
                show: true,
                position: "left",
                axisLabel: "Voltage"
            }, {
                show: true,
                position: "right",
                axisLabel: "Current"
            }, {
                show: true,
                position: "right",
                axisLabel: "Angle"
            }

            ],
            xaxis: {
                mode: "time",
                timeformat: "%H:%M:%S",
                timezone: "browser"
            },
            legend: {
                show: true,
                container: $('#legend'),
                labelFormatter: function (label, series) {
                    for (var i in measurementDetails) {
                        if (measurementDetails[i].SignalID == label)
                            return measurementDetails[i].SignalReference.split("!")[measurementDetails[i].SignalReference.split("!").length - 1] + " - " + measurementDetails[i].SignalAcronym;
                    }

                },
                noColumns: 1,
                margin: 5
            }
        });

    }

    // used to populate statistic modals
    function getStats() {
        if (!hubIsConnected)
            return;

        dataHub.getStats().done(function (data) {
            $.each(data, function (iter, d) {
                for (var i in stats) {
                    if (stats[i].id == d.ID) {
                        stats[i].value = d.Value;
                        stats[i].timestamp = d.Timestamp;

                        if (stats[i].deviceAcronym != null) {
                            if ($('#des' + d.ID).text().indexOf("Boolean") >= 0) $('#val' + d.ID).text(Boolean(d.Value));
                            else $('#val' + d.ID).text(d.Value);
                            $('#time' + d.ID).text(new Date(d.Timestamp).formatDate(DateTimeFormat));
                            if (stats[i].description == "Device statistic for Number of measurements received from device during last reporting interval.") {
                                if (stats[i].value != null && stats[i].value > 0) {
                                    $('#img' + stats[i].deviceAcronym.replace(/!/g, "-").replace(stats[i].deviceAcronym.split('!')[0] + '-', "")).attr("src", "@Url.Content("~/Images/StatusLights/Small/greenlight.png")");
                                } else {
                                    $('#img' + stats[i].deviceAcronym.replace(/!/g, "-").replace(stats[i].deviceAcronym.split('!')[0] + '-', "")).attr("src", "@Url.Content("~/Images/StatusLights/Small/redlight.png")");
                                }
                            }
                            if (stats[i].description == "Subscriber statistic for Number of processed measurements reported by the subscriber during last reporting interval.") {
                                if (stats[i].value != null && stats[i].value > 0) {
                                    $('#img' + stats[i].deviceAcronym.replace(/!/g, "-").replace(stats[i].deviceAcronym.split('!')[0] + '-', "")).attr("src", "@Url.Content("~/Images/StatusLights/Small/greenlight.png")");
                                } else {
                                    $('#img' + stats[i].deviceAcronym.replace(/!/g, "-").replace(stats[i].deviceAcronym.split('!')[0] + '-', "")).attr("src", "@Url.Content("~/Images/StatusLights/Small/redlight.png")");
                                }
                            }


                        }

                        break;
                    }
                }



            });

            //console.log(stats);
        }).fail(function (error) {
            showErrorMessage(error);
        });

        setTimeout(getStats, statRefreshInterval);
    }

    // used to get data and update the plot
    function update() {
        if (!hubIsConnected)
            return;

        dataHub.getMeasurements().done(function (data) {

            $.each(data, function (i, d) {
                for (var j in plotData) {
                    if (plotData[j].label == d.ID) {
                        plotData[j].data.push([d.Timestamp, d.Value]);
                    }
                }
            });

            for (var i = 0; i < plotData.length; ++i) {
                var num = (plotData[i].data.length - dataPointNumber)
                for (var j = 0; j < num; ++j)
                    plotData[i].data.shift()

            }

            plot.setData(plotData);
            plot.setupGrid()
            plot.draw();
            setTimeout(update, dataRefreshInterval);
        }).fail(function (error) {
            showErrorMessage(error);
        });
    }

    // used to update the data subscriper filter string with a new filter string
    function updateFilter() {
        if (!hubIsConnected)
            return;

        var filterStr = "";


        plotData = [];



        $('input[type=checkbox]').each(function () {
            var arr = this.value.split('#');
            if (this.checked == true) {
                filterStr += arr[0] + ";";
                for (var i in plotDataTemplate) {
                    if (plotDataTemplate[i].label == arr[1])
                        plotData.push(plotDataTemplate[i]);
                }
            }
        });

        setCookie("filterStr", filterStr, 365);
        dataHub.updateFilters(filterStr);
        plot.setData(plotData);
        plot.setupGrid();
        plot.draw();
    }

    // used to reset the data subscriper filter string to an empty filter string
    function resetFilter() {
        if (!hubIsConnected)
            return;

        var filterStr = "";

        setCookie("filterStr", filterStr, 365);

        for (var i = 0; i < plotData.length; ++i) {
            plotData[i].data = [];
        }

        $('input[type=checkbox]').each(function () {
            if (this.checked == true)
                this.checked = false;
        });

        dataHub.updateFilters(filterStr);
        plot.setData(plotData);
        plot.setupGrid()
        plot.draw();
    }

    // used to build statistic modals
    function buildModal(acronym) {
        $("#modals").append('<div id="' + acronym + '"></div>');
        html = '<div id="mod' + acronym + '" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal" aria-hidden="true">';
        html += '<div class="modal-dialog">';
        html += '<div class="modal-content">';
        html += '<div class="modal-header">';
        html += '<a class="close" data-dismiss="modal">×</a>';
        html += '<h4>' + acronym + ' Statistics</h4>'
        html += '</div>';
        html += '<div  class="modal-body" style="height: 300px; overflow-y: scroll" >';
        html += '<table id="stat' + acronym + '" class="table" style="font-size: x-small">';
        html += '<thead>Run-time Statistics:<thead />';
        html += '<tr><th>ID</th><th>Statistic</th><th>Value</th><th>TimeTag</th></tr>';
        html += '</table>';
        html += '</div>';
        html += '<div class="modal-footer">';
        html += '<span class="btn" data-dismiss="modal">Close</span>'; // close button
        html += '</div>';  // footer
        html += '</div>';  // modalWindow
        $('#' + acronym).html(html);
        $("#mod" + acronym).modal({ 'show': false });
    }

    // used to set cookies
    function setCookie(cname, cvalue, exdays) {
        var d = new Date();
        d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
        var expires = "expires=" + d.toUTCString();
        //console.log(cname + "=" + cvalue.replace(/;/g, '#') + "; " + expires);
        document.cookie = cname + "=" + cvalue.replace(/;/g, '#') + "; " + expires;
    }

    // used to get cookies
    function getCookie(cname) {
        var name = cname + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') {
                c = c.substring(1);
            }
            if (c.indexOf(name) == 0) {
                return c.substring(name.length, c.length);
            }
        }
        return "";
    }

    // used to check cookie
    function checkCookie() {
        if (!hubIsConnected)
            return;

        filterStr = getCookie("filterStr").replace(/#/g, ';');
        $('#filterstring').val(filterStr);
        //console.log(filterStr);

        dataRefreshInterval = getCookie("dataRefreshInterval");
        if (dataRefreshInterval == "") dataRefreshInterval = 250;
        $('#refreshinterval').val(dataRefreshInterval);

        statRefreshInterval = getCookie("statRefreshInterval");
        if (statRefreshInterval == "") statRefreshInterval = 10000;
        $('#statrefreshinterval').val(statRefreshInterval);

        dataPointNumber = getCookie("dataPointNumber");
        if (dataPointNumber == "") dataPointNumber = 300;
        $('#datapoints').val(dataPointNumber);

        var arr = filterStr.split(';');

        for (var i in arr) {
            $('input[type=checkbox]').each(function () {
                if (this.id == 'cb' + arr[i]) {
                    this.checked = true;
                    var arr2 = this.value.split('#')
                    for (var j in plotDataTemplate) {
                        if (plotDataTemplate[j].label == arr2[1])
                            plotData.push(plotDataTemplate[j]);

                    }

                }
            });
        }

        dataHub.updateFilters(filterStr);
    }

    // used to update settings
    function updateSettings() {
        dataRefreshInterval = $('#refreshinterval').val();
        statRefreshInterval = $('#statrefreshinterval').val();
        dataPointNumber = $('#datapoints').val();
        filterStr = $('#filterstring').val();

        setCookie("dataRefreshInterval", dataRefreshInterval, 365);
        setCookie("statRefreshInterval", statRefreshInterval, 365);
        setCookie("dataPointNumber", dataPointNumber, 365);
        setCookie("filterStr", filterStr, 365);

    }

    // sets heights of various objects
    function setHeights() {
        const height = calculateRemainingBody();
        $('#sidebar').css('height', height);
        $('#graphwrapper').css('height', height);
        $('#placeholder').css('height', height * 0.8);
    }

    function calculateRemainingBody() {
        // Calculation based on content in _Layout.cshtml
        return $(window).height();
    }


</script>

</html>
